package com.jh.loadbalance.client.custom.rest;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.stream.Stream;

/**
 * @author Administrator
 * @projectName newforward
 * @title
 * @package com.jh.loadbalance.client.custom.rest
 * @description
 * @date 2019/8/26 21:53
 */
public class RestClientRegister implements ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware {

    private BeanFactory beanFactory;

    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

        Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableRestClient.class.getName());

        ClassLoader classLoader = metadata.getClass().getClassLoader();

        // Map 过滤
        final Class<?>[] clients = attributes == null ? new Class[]{} : (Class<?>[]) attributes.get("clients");
        Stream.of(clients)
                // 过滤是接口的，因为class[]里面包含的可能有抽象类或者其他东西
                .filter(Class::isInterface)
                // 过滤含有 @RestClient 的接口类
                .filter(interfaceClass -> AnnotationUtils.findAnnotation(interfaceClass, RestClient.class) != null)
                .forEach(restClientClass -> {

                    // 获取 @RestClient 元信息
                    RestClient restClient = AnnotationUtils.findAnnotation(restClientClass, RestClient.class);

                    String serviceName = environment.resolvePlaceholders(restClient.name());

                    // 获取代理对象
                    Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{restClientClass}, new MappingMethodInvocationHandler(beanFactory, serviceName));

                    // 将 @RestClient 接口代理实现注册为 Bean
                    String beanName = "RestClient." + serviceName;

                    // SingletonBeanRegistry，单例Bean注册
                    if (registry instanceof SingletonBeanRegistry) {
                        SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry) registry;
                        singletonBeanRegistry.registerSingleton(beanName, proxy);
                    }

                    // FactoryBean 实现
                    //registerBean(registry, restClientClass, proxy, beanName);
                });
    }

    private void registerBean(BeanDefinitionRegistry registry, Class<?> restClientClass, Object proxy, String beanName){

        // 创建 BeanDefinitionBuilder，类似在xml构造一样
        /**
         * <Bean class="User">
         *      <constructor-arg>${}</constructor-arg>
         *     </Bean>
         */
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RectClientFactoryBean.class);
        beanDefinitionBuilder.addConstructorArgValue(restClientClass);
        beanDefinitionBuilder.addConstructorArgValue(proxy);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        // 注册一个Bean
        registry.registerBeanDefinition(beanName, beanDefinition);
    }

    private static class RectClientFactoryBean implements FactoryBean {

        private final Class<?> restClientClass;
        private final Object restClientObj;

        private RectClientFactoryBean(Class<?> restClientClass, Object restClientObj) {
            this.restClientClass = restClientClass;
            this.restClientObj = restClientObj;
        }

        @Override
        public Object getObject() throws Exception {
            return restClientObj;
        }

        @Override
        public Class<?> getObjectType() {
            return restClientClass;
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setEnvironment(Environment environment) {
        // 设置Environment，用于解释动态服务名
        this.environment = environment;
    }
}
